import torch
import cv2
import numpy as np
import torchvision

#
# class Test:
#     def __init__(self):
#         modelPath = "../yolov5"
#         weightsPath = "weights/yolov5l.pt"
#         self.model = torch.hub.load(modelPath,
#                                     'custom',
#                                     weightsPath,
#                                     source='local')  # or yolov5n - yolov5x6, custom
#         # self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
#
#     def detect(self, img):
#         results = self.model(img, size=640)
#         preds = results.xyxy[0].cpu().numpy()
#         # nms抑制
#         keep = torchvision.ops.nms(torch.tensor(preds[:, :4]), torch.tensor(preds[:, 4]), iou_threshold=0.1)
#         preds = preds[keep, :]
#         return preds
#
#
# if __name__ == "__main__":
#     test = Test()
#
#     img = cv2.imread("D:/work/VOC2007/JPEGImages/Abyssinian cat/naver_0001.jpg")
#     preds = test.detect(img)
#
#     for pred in preds:
#         # 获取检测框坐标和置信度
#         x1, y1, x2, y2 = pred[:4].astype(np.int32)
#         conf = pred[4]
#
#         # 绘制检测框
#         cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
#
#         # 在检测框上方显示置信度
#         cv2.putText(img, f'{conf:.2f}', (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
#
#     # 显示图片
#     cv2.imshow('Detection Result', img)
#     cv2.waitKey(0)
#     cv2.destroyAllWindows()
# cv2.imshow('YOLO', np.squeeze(results.render()))
# while True:
#     cv2.imshow('YOLO', np.squeeze(results.render()))
#     if cv2.waitKey(10) & 0xFF == ord('q'):
#         break
# cv2.destroyAllWindows()
# cap = cv2.VideoCapture(0)
#
# while cap.isOpened():
#     ret, frame = cap.read()
#
#     # Make detections
#     results = test.detect(frame)
#     cv2.imshow('YOLO', np.squeeze(results.render()))
#
#     if cv2.waitKey(10) & 0xFF == ord('q'):
#         break
#
# cap.release()
# cv2.destroyAllWindows()


import torch
import numpy as np
from models.experimental import attempt_load
from utils.general import non_max_suppression, scale_coords, letterbox
from utils.torch_utils import select_device
import cv2
from random import randint


class Detector(object):

    def __init__(self):
        self.img_size = 640
        self.threshold = 0.4
        self.max_frame = 160
        self.init_model()

    def init_model(self):

        self.weights = 'weights/yolov5m.pt'
        self.device = '0' if torch.cuda.is_available() else 'cpu'
        self.device = select_device(self.device)
        model = attempt_load(self.weights, map_location=self.device)
        model.to(self.device).eval()
        model.half()
        # torch.save(model, 'test.pt')
        self.m = model
        self.names = model.module.names if hasattr(
            model, 'module') else model.names
        self.colors = [
            (randint(0, 255), randint(0, 255), randint(0, 255)) for _ in self.names
        ]

    def preprocess(self, img):

        img0 = img.copy()
        img = letterbox(img, new_shape=self.img_size)[0]
        img = img[:, :, ::-1].transpose(2, 0, 1)
        img = np.ascontiguousarray(img)
        img = torch.from_numpy(img).to(self.device)
        img = img.half()  # 半精度
        img /= 255.0  # 图像归一化
        if img.ndimension() == 3:
            img = img.unsqueeze(0)

        return img0, img

    def plot_bboxes(self, image, bboxes, line_thickness=None):
        tl = line_thickness or round(
            0.002 * (image.shape[0] + image.shape[1]) / 2) + 1  # line/font thickness
        for (x1, y1, x2, y2, cls_id, conf) in bboxes:
            color = self.colors[self.names.index(cls_id)]
            c1, c2 = (x1, y1), (x2, y2)
            cv2.rectangle(image, c1, c2, color,
                          thickness=tl, lineType=cv2.LINE_AA)
            tf = max(tl - 1, 1)  # font thickness
            t_size = cv2.getTextSize(
                cls_id, 0, fontScale=tl / 3, thickness=tf)[0]
            c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
            cv2.rectangle(image, c1, c2, color, -1, cv2.LINE_AA)  # filled
            cv2.putText(image, '{} ID-{:.2f}'.format(cls_id, conf), (c1[0], c1[1] - 2), 0, tl / 3,
                        [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
        return image

    def detect(self, im):

        im0, img = self.preprocess(im)

        pred = self.m(img, augment=False)[0]
        pred = pred.float()
        pred = non_max_suppression(pred, self.threshold, 0.3)

        pred_boxes = []
        image_info = {}
        count = 0
        for det in pred:
            if det is not None and len(det):
                det[:, :4] = scale_coords(
                    img.shape[2:], det[:, :4], im0.shape).round()

                for *x, conf, cls_id in det:
                    lbl = self.names[int(cls_id)]
                    x1, y1 = int(x[0]), int(x[1])
                    x2, y2 = int(x[2]), int(x[3])
                    pred_boxes.append(
                        (x1, y1, x2, y2, lbl, conf))
                    count += 1
                    key = '{}-{:02}'.format(lbl, count)
                    image_info[key] = ['{}×{}'.format(
                        x2-x1, y2-y1), np.round(float(conf), 3)]

        im = self.plot_bboxes(im, pred_boxes)
        return im, image_info


detect = Detector()
img = cv2.imread("D:/work/VOC2007/JPEGImages/Abyssinian cat/naver_0001.jpg")
detect.detect(img)